#ifndef INTERPRETER
#define INTERPRETER

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "poly.h"
#include "stack.h"
#include "varpool.h"

VarPool *pool;

// 运算符优先级
int Piriority(char oprt)
{
    int res;
    switch (oprt)
    {
    case '(':
        res = 0;
        break;

    case '+':
    case '-':
        res = 1;
        break;

    case '*':
        res = 2;
        break;

    case '^':
        res = 3;
        break;

    case '\'': // 求导
        res = 4;
        break;

    case '_': // 取负
        res = 4;
        break;

    default:
        res = -1;
        break;
    }
    return res;
}

// 将表示一元多项式的字符串转化为多项式
Poly *Convert(const char *str)
{
    Poly *poly = NULL;

    Stack *stack = NewStack();
    Stack *operators = NewStack();
    Stack *polys = NewStack();

    char buff[20] = "";
    int error_flag = 0;
    int allow_sign = 1;
    int allow_diff = 0;

    // 将输入转化为逆波兰表达式

    for (const char *ch = str; *ch != '\0' && !error_flag; ch++)
    {
        if (isdigit(*ch) || *ch == '.' || isalpha(*ch) || *ch == '_')
        {
            allow_sign = 0;
            allow_diff = 1;
            strncat(buff, ch, 1);
        }
        else if ((*ch == '+' || *ch == '-') && allow_sign)
        {
            allow_sign = 0;
            allow_diff = 0;
            if (*ch == '-')
            {
                Push(operators, NewElem(NULL, 0, '_'));
            }
        }
        else
        {
            if (strlen(buff))
            {
                if (isdigit(buff[0]) || buff[0] == '.')
                {
                    Push(stack, NewElem(NULL, atof(buff), '\0'));
                    char *temp;
                    for (temp = buff; *temp != '\0'; temp++)
                    {
                        if (!(isdigit(*temp) || *temp == '.'))
                        {
                            break;
                        }
                    }
                    if (*temp != '\0')
                    {
                        Var *temp_var = GetVar(pool, temp);
                        if (temp_var && temp_var->poly)
                        {
                            while (Size(operators) && Piriority('*') <= Piriority(Top(operators)->oprt))
                            {
                                Push(stack, Pop(operators));
                            }
                            Push(operators, NewElem(NULL, 0, '*'));
                            Push(stack, NewElem(ClonePoly(temp_var->poly), 0, '\0'));
                        }
                    }
                }
                else
                {
                    Var *temp = GetVar(pool, buff);
                    if (temp && temp->poly)
                    {
                        Push(stack, NewElem(ClonePoly(temp->poly), 0, '\0'));
                    }
                    else
                    {
                        error_flag = 1;
                        break;
                    }
                }
                strcpy(buff, "");
            }
            switch (*ch)
            {
            case '(':
                allow_sign = 1;
                allow_diff = 0;
                Push(operators, NewElem(NULL, 0, *ch));
                break;

            case ')':
                allow_sign = 0;
                allow_diff = 1;
                while (Size(operators) && Top(operators)->oprt != '(')
                {
                    Push(stack, Pop(operators));
                }
                if (Size(operators) == 0)
                {
                    error_flag = 1;
                    break;
                }
                free(Pop(operators));
                break;

            case '+':
            case '-':
            case '*':
            case '^':
                allow_sign = 1;
                allow_diff = 0;
                while (Size(operators) && Piriority(*ch) <= Piriority(Top(operators)->oprt))
                {
                    Push(stack, Pop(operators));
                }
                Push(operators, NewElem(NULL, 0, *ch));
                break;

            case '\'':
                if (allow_diff)
                {
                    allow_sign = 0;
                    allow_diff = 1;
                    Push(operators, NewElem(NULL, 0, *ch));
                }
                else
                {
                    error_flag = 1;
                }
                break;

            case ' ':
            case '#':
                allow_sign = 0;
                break;

            default:
                error_flag = 1;
                break;
            }
        }
    }
    if (!error_flag)
    {
        while (Size(operators))
        {
            Push(stack, Pop(operators));
        }

        if (Size(stack) == 0)
        {
            error_flag = 1;
        }

        // 对逆波兰表达式求值
        for (int i = 0; i < Size(stack) && !error_flag; i++)
        {
            Elem *e = stack->data[i];
            if (IsPoly(e) || IsNum(e))
            {
                Push(polys, e);
            }
            else if (IsOprt(e))
            {
                Elem *e1, *e2;
                switch (e->oprt)
                {
                case '+':
                    e2 = Pop(polys);
                    e1 = Pop(polys);
                    if (!(e1 && e2))
                    {
                        error_flag = 1;
                        break;
                    }

                    if (IsPoly(e1) && IsPoly(e2))
                    {
                        AddPoly(e1->poly, e2->poly);
                        Push(polys, e1);
                    }
                    else if (IsPoly(e1) && IsNum(e2))
                    {
                        AddNum(e1->poly, e2->num);
                        Push(polys, e1);
                    }
                    else if (IsNum(e1) && IsNum(e2))
                    {
                        e1->num += e2->num;
                        Push(polys, e1);
                    }
                    else if (IsNum(e1) && IsPoly(e2))
                    {
                        AddNum(e2->poly, e1->num);
                        Push(polys, e2);
                    }
                    else
                    {
                        error_flag = 1;
                        break;
                    }
                    break;

                case '-':
                    e2 = Pop(polys);
                    e1 = Pop(polys);
                    if (!(e1 && e2))
                    {
                        error_flag = 1;
                        break;
                    }

                    if (IsPoly(e1) && IsPoly(e2))
                    {
                        SubPoly(e1->poly, e2->poly);
                        Push(polys, e1);
                    }
                    else if (IsPoly(e1) && IsNum(e2))
                    {
                        AddNum(e1->poly, -(e2->num));
                        Push(polys, e1);
                    }
                    else if (IsNum(e1) && IsNum(e2))
                    {
                        e1->num -= e2->num;
                        Push(polys, e1);
                    }
                    else if (IsNum(e1) && IsPoly(e2))
                    {
                        AddNum(e2->poly, -(e1->num));
                        NegPoly(e2->poly);
                        Push(polys, e2);
                    }
                    else
                    {
                        error_flag = 1;
                        break;
                    }
                    break;

                case '*':
                    e2 = Pop(polys);
                    e1 = Pop(polys);
                    if (!(e1 && e2))
                    {
                        error_flag = 1;
                        break;
                    }

                    if (IsPoly(e1) && IsPoly(e2))
                    {
                        MulPoly(e1->poly, e2->poly);
                        Push(polys, e1);
                    }
                    else if (IsPoly(e1) && IsNum(e2))
                    {
                        MulNum(e1->poly, e2->num);
                        Push(polys, e1);
                    }
                    else if (IsNum(e1) && IsNum(e2))
                    {
                        e1->num *= e2->num;
                        Push(polys, e1);
                    }
                    else if (IsNum(e1) && IsPoly(e2))
                    {
                        MulNum(e2->poly, e1->num);
                        Push(polys, e2);
                    }
                    else
                    {
                        error_flag = 1;
                        break;
                    }
                    break;

                case '^':
                    e2 = Pop(polys);
                    e1 = Pop(polys);
                    if (!(e1 && e2))
                    {
                        error_flag = 1;
                        break;
                    }

                    if (IsPoly(e1) && IsNum(e2))
                    {
                        if (PowNum(e1->poly, e2->num))
                        {
                            Push(polys, e1);
                        }
                        else
                        {
                            error_flag = 1;
                            break;
                        }
                    }
                    else if (IsNum(e1) && IsNum(e2))
                    {
                        e1->num = pow(e1->num, e2->num);
                        Push(polys, e1);
                    }
                    else
                    {
                        error_flag = 1;
                        break;
                    }
                    break;

                case '\'':
                    e1 = Pop(polys);
                    if (!e1)
                    {
                        error_flag = 1;
                        break;
                    }
                    if (IsPoly(e1))
                    {
                        DiffPoly(e1->poly);
                        Push(polys, e1);
                    }
                    else if (IsNum(e1))
                    {
                        e1->num = 0;
                        Push(polys, e1);
                    }
                    else
                    {
                        error_flag = 1;
                        break;
                    }
                    break;

                case '_':
                    e1 = Pop(polys);
                    if (!e1)
                    {
                        error_flag = 1;
                        break;
                    }
                    if (IsPoly(e1))
                    {
                        NegPoly(e1->poly);
                        Push(polys, e1);
                    }
                    else if (IsNum(e1))
                    {
                        e1->num = -e1->num;
                        Push(polys, e1);
                    }
                    else
                    {
                        error_flag = 1;
                        break;
                    }

                default:
                    break;
                }
            }
        }

        if (Size(polys) != 1)
        {
            error_flag = 1;
        }

        if (!error_flag)
        {
            Elem *elem = Pop(polys);
            if (IsPoly(elem))
            {
                poly = ClonePoly(elem->poly);
            }
            else if (IsNum(elem))
            {
                poly = NewPoly();
                if (elem->num)
                {
                    AddNode(poly, NewNode(elem->num, 0));
                }
            }
        }
    }

    // 清空栈，释放内存空间
    ClearStack(stack);
    free(stack);
    ClearStack(operators);
    free(operators);
    free(polys);

    return poly;
}

// 输出多项式
void PrintPoly(Poly *poly)
{
    int length = 0;
    for (Node *node = poly->head; node->next; node = node->next)
    {
        length++;
    }
    if (length <= 10)
    {
        char line1[100] = {'\0'}, line2[100] = {'\0'};
        int len1 = 0, len2 = 0;
        if (!poly->head->next)
        {
            sprintf(line2 + len2, "%d", 0);
        }
        for (Node *node = poly->head; node->next; node = node->next)
        {

            if (node == poly->head)
            {
                if (node->next->e == 0)
                {
                    sprintf(line2 + len2, floor(node->next->c) == node->next->c ? "%.0lf" : "%.2lf", node->next->c);
                }
                else
                {

                    if (node->next->c == 1)
                    {
                    }
                    else if (node->next->c == -1)
                    {
                        sprintf(line2 + len2, "-");
                    }
                    else
                    {
                        sprintf(line2 + len2, floor(node->next->c) == node->next->c ? "%.0lf" : "%.2lf", node->next->c);
                    }
                }
            }
            else
            {
                if (node->next->e == 0)
                {
                    sprintf(line2 + len2, floor(node->next->c) == node->next->c ? "%+.0lf" : "%+.2lf", node->next->c);
                }
                else
                {
                    if (node->next->c == 1)
                    {
                        sprintf(line2 + len2, "+");
                    }
                    else if (node->next->c == -1)
                    {
                        sprintf(line2 + len2, "-");
                    }
                    else
                    {
                        sprintf(line2 + len2, floor(node->next->c) == node->next->c ? "%+.0lf" : "%+.2lf", node->next->c);
                    }
                }
            }
            len2 = strlen(line2);
            if (node->next->e != 0)
            {
                sprintf(line2 + len2, "x");
                len2 = strlen(line2);
            }
            memset(line1 + len1, ' ', len2 - len1);
            len1 = len2;
            if (node->next->e != 0 && node->next->e != 1)
            {
                sprintf(line1 + len1, floor(node->next->e) == node->next->e ? "%.0lf" : "%.2lf", node->next->e);
                len1 = strlen(line1);
            }
            memset(line2 + len2, ' ', len1 - len2);
            len2 = len1;
        }
        printf("%s\n%s\n", line1, line2);
    }
    else
    {
        if (!poly->head->next)
        {
            printf("%d", 0);
        }
        for (Node *node = poly->head; node->next; node = node->next)
        {

            if (node == poly->head)
            {
                if (node->next->e == 0)
                {
                    printf(floor(node->next->c) == node->next->c ? "%.0lf" : "%.2lf", node->next->c);
                }
                else
                {

                    if (node->next->c == 1)
                    {
                    }
                    else if (node->next->c == -1)
                    {
                        printf("-");
                    }
                    else
                    {
                        printf(floor(node->next->c) == node->next->c ? "%.0lf" : "%.2lf", node->next->c);
                    }
                }
            }
            else
            {
                if (node->next->e == 0)
                {
                    printf(floor(node->next->c) == node->next->c ? "%+.0lf" : "%+.2lf", node->next->c);
                }
                else
                {
                    if (node->next->c == 1)
                    {
                        printf("+");
                    }
                    else if (node->next->c == -1)
                    {
                        printf("-");
                    }
                    else
                    {
                        printf(floor(node->next->c) == node->next->c ? "%+.0lf" : "%+.2lf", node->next->c);
                    }
                }
            }
            if (node->next->e != 0)
            {
                printf("x");
            }
            if (node->next->e != 0 && node->next->e != 1)
            {
                printf("^");
                printf(floor(node->next->e) == node->next->e ? "%.0lf" : "%.2lf", node->next->e);
            }
        }
        printf("\n");
    }
}

// 执行命令行
int Interpret(char *command)
{
    char left[100], right[100];
    Poly *poly;

    char *equal = NULL, *colon = NULL;

    for (char *ch = command; *ch != '\0'; ch++)
    {
        if (*ch == '=')
        {
            equal = ch;
        }
        else if (*ch == ':')
        {
            colon = ch;
        }
    }
    if (equal && colon)
    {
        return -1;
    }

    if (equal)
    {
        // 含有等于号
        if (equal - command == 0)
        {
            return -1;
        }

        int length = equal - command;
        strncpy(left, command, length);
        left[length] = '\0';

        // 去除空格
        for (int i = 0; i < equal - command; i++)
        {
            if (left[i] == ' ')
            {
                left[i] = '\0';
                break;
            }
        }

        if (!(isalpha(left[0]) || left[0] == '_'))
        {
            return -1;
        }
        for (char *temp = left; *temp != '\0'; temp++)
        {
            if (!(isalpha(*temp) || isdigit(*temp) || *temp == '_'))
            {
                return -1;
            }
        }
        if (!strcmp(left, "x") || !strcmp(left, "ans"))
        {
            return -1;
        }

        strcpy(right, equal + 1);
        strcat(right, "#");

        poly = Convert(right);
        if (!poly)
        {
            return -1;
        }
        PutVar(pool, NewVar("ans", poly));
        PutVar(pool, NewVar(left, ClonePoly(poly)));
        PrintPoly(poly);
    }
    else if (colon)
    {
        // 含有冒号
        if (colon - command == 0)
        {
            return -1;
        }

        int length = colon - command;
        strncpy(left, command, length);
        left[length] = '\0';
        
        strcat(left, "#");

        poly = Convert(left);
        if (!poly)
        {
            return -1;
        }

        strcpy(right, colon + 1);
        EvalPoly(poly, atof(right));
        PrintPoly(poly);
        PutVar(pool, NewVar("ans", poly));
    }
    else
    {
        // 既没有等于号也没有冒号
        strcpy(right, command);
        strcat(right, "#");

        poly = Convert(right);
        if (!poly)
        {
            return -1;
        }
        PrintPoly(poly);
        PutVar(pool, NewVar("ans", poly));
    }
    return 0;
}

#endif
